package com.gandong8.es.tool.util;

import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.gandong8.es.tool.annotation.EsMapping;
import com.gandong8.es.tool.annotation.EsSetting;
import com.gandong8.es.tool.core.EsDoc;
import com.gandong8.es.tool.domain.EsDataType;
import com.xiaoleilu.hutool.date.DateUtil;
import org.apache.commons.lang3.StringUtils;
//import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.common.settings.Settings;

import java.lang.reflect.Field;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class EsUtil {

    public static CreateIndexRequest classToRequest(Class<?> clazz) {
        CreateIndexRequest request = buildSetting(clazz);
        buildMapping(request, clazz);
        return request;
    }

    public static <T> String parseId(T obj) {
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (!field.isAnnotationPresent(EsMapping.class)) {
                continue;
            }
            field.setAccessible(true);
            EsMapping esMapping = field.getAnnotation(EsMapping.class);
            if (!esMapping.document_id()) {
                continue;
            }
            Object fieldObj = null;
            try {
                fieldObj = field.get(obj);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            if(fieldObj == null) {
                return null;
            }
            return fieldObj.toString();
        }
        return null;
    }

    public static String getSortTime(Class<?> clazz) {
        Field[] fields = clazz.getDeclaredFields();
        boolean isSnake = isSnake(clazz);
        for (Field field : fields) {
            if (!field.isAnnotationPresent(EsMapping.class)) {
                continue;
            }
            field.setAccessible(true);
            EsMapping esMapping = field.getAnnotation(EsMapping.class);
            if(esMapping.sort_time()) {
                return isSnake? translateFieldName(field.getName()):field.getName();
            }
        }
        return null;
    }



    public static Map<String, Object> parseObjValue(EsDoc obj) {
        Class clazz = obj.getClass();
        Field[] fields = clazz.getDeclaredFields();
        Map<String, Object> map = new HashMap<>();
        boolean nameSnake = isSnake(clazz);
        for (Field field : fields) {
            if (!field.isAnnotationPresent(EsMapping.class)) {
                continue;
            }
            field.setAccessible(true);
            Object fieldObj = null;
            try {
                fieldObj = field.get(obj);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            if(fieldObj == null) {
                continue;
            }
            map.put(nameSnake? translateFieldName(field.getName()):field.getName(), fieldObj);
        }
        return map;
    }

    public static String parseIndexName(Class<?> clazz) {
        return parseIndexName(clazz, null);
    }

    public static String parseIndexType(Class<?> clazz) {
        EsSetting esSetting = clazz.getAnnotation(EsSetting.class);
        return esSetting.index_type();
    }

    public static String parseIndexNameWithType(Class<?> clazz) {
        EsSetting esSetting = clazz.getAnnotation(EsSetting.class);
        String name = parseIndexName(clazz, esSetting);
        return name + "," + esSetting.index_type();
    }

    public static String parseIndexName(Class<?> clazz, EsSetting esSetting) {
        if (esSetting == null) {
            esSetting = clazz.getAnnotation(EsSetting.class);
        }
        if(StringUtils.isBlank(esSetting.index_date_format())) {
            return esSetting.provided_name();
        }
        String value = DateUtil.format(new Date(), esSetting.index_date_format());
        if(value == null) {
            return esSetting.provided_name();
        }
        return esSetting.provided_name() + value;
    }


    private static CreateIndexRequest buildSetting(Class<?> clazz) {
        EsSetting esSetting = clazz.getAnnotation(EsSetting.class);
        if(esSetting == null) {
            return null;
        }
        String indexName = parseIndexName(clazz, esSetting);
        CreateIndexRequest request = new CreateIndexRequest(indexName);
        Settings.Builder builder = Settings.builder();
        builder.put("index.number_of_shards", esSetting.number_of_shards());
        builder.put("index.number_of_replicas", esSetting.number_of_replicas());
        builder.put("index.merge.scheduler.max_thread_count", esSetting.merge_scheduler_max_thread_count());
        builder.put("index.refresh_interval", esSetting.refresh_interval());
        builder.put("index.translog.durability", esSetting.translog_durability());
        builder.put("index.translog.sync_interval", esSetting.translog_sync_interval());
        builder.put("index.translog.flush_threshold_size", esSetting.translog_flush_threshold_size());
        int analyzerLen = esSetting.custom_analyzer_name().length;
        if(analyzerLen > 0) {
            for(int i=0; i< analyzerLen; i++) {
                builder.put("index.analysis.analyzer." + esSetting.custom_analyzer_name()[i] + ".type", esSetting.custom_analyzer()[i].getType());
            }
        }
        request.settings(builder);
        return request;
    }


    private static void buildMapping(CreateIndexRequest request, Class<?> clazz) {
        Field[] fields = clazz.getDeclaredFields();
        Map<String, Object> propertiesMap = new HashMap<>();
        boolean nameSnake = isSnake(clazz);
        for (Field field : fields) {
            if (!field.isAnnotationPresent(EsMapping.class)) {
                continue;
            }
            field.setAccessible(true);
            EsMapping esMapping = field.getAnnotation(EsMapping.class);
            if(esMapping.document_id()) {
                continue;
            }
            Map<String, Object> map = new HashMap<>();
            map.put("type", esMapping.datatype().getType());
            if(StringUtils.isNotBlank(esMapping.copy_to())) {
                map.put("copy_to", esMapping.copy_to());
            }
            if(StringUtils.isNotBlank(esMapping.null_value())) {
                map.put("null_value", esMapping.null_value());
            }
            if(EsDataType.KEYWORD.equals(esMapping.datatype())) {
                map.put("ignore_above", esMapping.ignore_above());
            }
            else if(EsDataType.TEXT.equals(esMapping.datatype())) {
                map.put("analyzer", esMapping.analyzer().getType());
                map.put("search_analyzer", esMapping.search_analyzer().getType());
            }
            propertiesMap.put(nameSnake? translateFieldName(field.getName()):field.getName(), map);
        }
        Map<String, Object> jsonMap = new HashMap<>();
        Map<String, Object> indexMap = new HashMap<>();
        indexMap.put("properties", propertiesMap);
        jsonMap.put(request.index(), indexMap);
        //elasticsearch7默认不在支持指定索引类型，默认索引类型是_doc
        //Root mapping definition has unsupported parameters
        request.mapping(jsonMap);
        request.mapping(indexMap);
        //6.x
        //request.mapping(parseIndexType(clazz), indexMap);
    }

    private static boolean isSnake(Class<?> clazz) {
        EsSetting esSetting = clazz.getAnnotation(EsSetting.class);
        return esSetting.property_snake_case();
    }

    private static String translateFieldName(String fileName) {
        PropertyNamingStrategy.SnakeCaseStrategy aa = new PropertyNamingStrategy.SnakeCaseStrategy();
        return aa.translate(fileName);
    }
}
